﻿using Microsoft.AspNetCore.Http;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FreeDream.Infrastructure
{
    /// <summary>
    /// API统一异常处理中间件
    /// </summary>
    public class AppExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private AppExceptionHandlerOption _option = new AppExceptionHandlerOption();
        private readonly IDictionary<int, string> _exceptionStatusCodeDic;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        /// <param name="log"></param>
        public AppExceptionHandlerMiddleware(RequestDelegate next)
        {
            _next = next;

            _exceptionStatusCodeDic = new Dictionary<int, string>
            {
                { 401, "未授权的请求" },
                { 404, "找不到该页面" },
                { 403, "访问被拒绝" },
                { 500, "服务器发生意外的错误" }
            };
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            Exception exception = null;
            try
            {
                await _next(context); //调用管道执行下一个中间件
            }
            catch (AppException ex)
            {
                context.Response.StatusCode = StatusCodes.Status200OK;
                var apiResponse = new ApiResponse() { Code = 500, Message = ex.ErrorMsg };
                var serializerResult = JsonConvert.SerializeObject(apiResponse);
                context.Response.ContentType = "application/json;charset=utf-8";
                await context.Response.WriteAsync(serializerResult);
            }
            catch (Exception ex)
            {
                context.Response.Clear();
                context.Response.StatusCode = StatusCodes.Status500InternalServerError; //发生未捕获的异常，手动设置状态码
                exception = ex;
            }
            finally
            {
                if (_exceptionStatusCodeDic.ContainsKey(context.Response.StatusCode) && !context.Items.ContainsKey("ExceptionHandled")) //预处理标记
                {
                    string errorMsg;
                    if (context.Response.StatusCode == 500 && exception != null)
                    {
                        errorMsg = $"{(exception.InnerException != null ? exception.InnerException.Message : exception.Message)}";
                        //await Log.InsertErrorLog(errorMsg, "", exception);
                    }
                    else
                    {
                        errorMsg = _exceptionStatusCodeDic[context.Response.StatusCode];
                    }
                    exception = new Exception(errorMsg);
                }
                if (exception != null)
                {
                    var handleType = _option.HandleType;
                    if (handleType == AppExceptionHandleType.Both) //根据Url关键字决定异常处理方式
                    {
                        var requestPath = context.Request.Path;
                        handleType = _option.JsonHandleUrlKeys != null && _option.JsonHandleUrlKeys.Count(
                                         k => requestPath.StartsWithSegments(k, StringComparison.CurrentCultureIgnoreCase)) > 0
                            ? AppExceptionHandleType.JsonHandle
                            : AppExceptionHandleType.PageHandle;
                    }
                    if (handleType == AppExceptionHandleType.JsonHandle)
                        await JsonHandle(context, exception);
                    else
                        await PageHandle(context, exception, _option.ErrorHandingPath);
                }
            }
        }

        /// <summary>
        /// 统一格式响应类
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private ApiResponse GetApiResponse(Exception ex)
        {
            return new ApiResponse() { Code = 500, Message = ex.Message };
        }

        /// <summary>
        /// 处理方式：返回Json格式
        /// </summary>
        /// <param name="context"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private async Task JsonHandle(HttpContext context, Exception ex)
        {
            var apiResponse = GetApiResponse(ex);
            apiResponse.Code = context.Response.StatusCode;
            JsonSerializerSettings settings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            };

            var serializerResult = JsonConvert.SerializeObject(apiResponse, settings);

            context.Response.ContentType = "application/json;charset=utf-8";
            await context.Response.WriteAsync(serializerResult);
        }

        /// <summary>
        /// 处理方式：跳转网页
        /// </summary>
        /// <param name="context"></param>
        /// <param name="ex"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        private async Task PageHandle(HttpContext context, Exception ex, PathString path)
        {
            context.Items.Add("Exception", ex);
            var originPath = context.Request.Path;
            context.Request.Path = path;   //设置请求页面为错误跳转页面
            try
            {
                await _next(context);
            }
            catch
            {

            }
            finally
            {
                context.Request.Path = originPath;   //恢复原始请求页面
            }
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public class AppExceptionHandlerOption
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="handleType"></param>
        /// <param name="jsonHandleUrlKeys"></param>
        /// <param name="errorHandingPath"></param>
        public AppExceptionHandlerOption(
            AppExceptionHandleType handleType = AppExceptionHandleType.JsonHandle,
            IList<PathString> jsonHandleUrlKeys = null,
            string errorHandingPath = "")
        {
            HandleType = handleType;
            JsonHandleUrlKeys = jsonHandleUrlKeys;
            ErrorHandingPath = errorHandingPath;
        }

        /// <summary>
        /// 异常处理方式
        /// </summary>
        public AppExceptionHandleType HandleType { get; set; }

        /// <summary>
        /// Json处理方式的Url关键字
        /// <para>仅HandleType=Both时生效</para>
        /// </summary>
        public IList<PathString> JsonHandleUrlKeys { get; set; }

        /// <summary>
        /// 错误跳转页面
        /// </summary>
        public PathString ErrorHandingPath { get; set; }
    }
    /// <summary>
    /// 错误处理方式
    /// </summary>
    public enum AppExceptionHandleType
    {
        /// <summary>
        /// 
        /// </summary>
        JsonHandle = 0,   //Json形式处理
        /// <summary>
        /// 
        /// </summary>
        PageHandle = 1,   //跳转网页处理
        /// <summary>
        /// 
        /// </summary>
        Both = 2          //根据Url关键字自动处理
    }
    /// <summary>
    /// 
    /// </summary>
    public class ApiResponse
    {
        /// <summary>
        /// 响应代码
        /// </summary>
        public int Code { get; set; }
        /// <summary>
        /// 响应消息内容
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// 返回的响应数据
        /// </summary>
        public object Data { get; set; }
    }
    /// <summary>
    /// 
    /// </summary>
    public class AppException : Exception
    {
        /// <summary>
        /// 
        /// </summary>
        public string ErrorMsg { get; set; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="errorMsg"></param>
        public AppException(string errorMsg)
        {
            ErrorMsg = errorMsg;
        }
    }
}
